package legumes.client.ui.widgets.window;

import java.util.List;

import legumes.client.evt.manager.EventBusManager;
import legumes.client.evt.store.PlaceDeliverySelectionChangedEvent;
import legumes.client.evt.store.PlaceDeliverySelectionChangedEventHandler;
import legumes.client.evt.store.StoreResearchByZipCodeChangedEvent;
import legumes.client.evt.store.StoreResearchByZipCodeChangedEventHandler;
import legumes.client.resources.PNResources;
import legumes.client.services.callbacks.GetStoreCallback;
import legumes.client.services.util.rpc.RPCCallSequencer;
import legumes.client.ui.util.wrapper.LatLngWrapper;
import legumes.client.ui.widgets.map.MapDrawingCircleUtils;
import legumes.client.ui.widgets.map.MapFactory;
import legumes.shared.domain.HomePlaceDelivery;
import legumes.shared.domain.PlaceDelivery;
import legumes.shared.domain.Store;

import com.google.gwt.ajaxloader.client.AjaxLoader;
import com.google.gwt.ajaxloader.client.ClientLocation;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.maps.client.InfoWindowContent;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.control.MapTypeControl;
import com.google.gwt.maps.client.control.SmallMapControl;
import com.google.gwt.maps.client.event.MapDragEndHandler;
import com.google.gwt.maps.client.event.MapZoomEndHandler;
import com.google.gwt.maps.client.event.MarkerClickHandler;
import com.google.gwt.maps.client.geocode.Geocoder;
import com.google.gwt.maps.client.geocode.LatLngCallback;
import com.google.gwt.maps.client.geom.LatLng;
import com.google.gwt.maps.client.overlay.Icon;
import com.google.gwt.maps.client.overlay.Marker;
import com.google.gwt.maps.client.overlay.MarkerOptions;
import com.smartgwt.client.widgets.Window;

public class StoreMapWindow extends Window {
	private MapWidget map;
	private int windowHeight;
	private int windowWidth;
	private Store selectedStore;
	
	public StoreMapWindow(int windowWidth_, int windowHeight_, final LatLng clLatLng_) {
		super();

		if(windowHeight_>0){
			windowHeight = windowHeight_;
		} else {
			windowHeight = com.google.gwt.user.client.Window.getClientHeight();
		}
		
//		if(windowWidth_>0){
			windowWidth = windowWidth_;
//		} else {
//			windowWidth = com.google.gwt.user.client.Window.getClientWidth();
//		}
		setTitle("<img src=\"" + PNResources.INSTANCE.mapIcon().getURL() + "\" />&nbsp;Rechercher un magasin");
		setWidth100();
		setHeight(windowHeight);
		
		setShowCloseButton(false);
        setShowMinimizeButton(true);
		setCanDrag(false);
		setCanDragReposition(false);
		
		setMargin(5);
		setShowMaximizeButton(true);
		initWindowProperties();
		
		/*
		 * Asynchronously loads the Maps API.
		 * 
		 * The first parameter should be a valid Maps API Key to deploy this
		 * application on a public server, but a blank key will work for an
		 * application served from localhost.
		 */
		MapFactory.getInstance().loadMapLibrary(
				new Runnable() {
					public void run() {
						buildUi(clLatLng_);
					}
				}
		);
	}
	
	protected void initWindowProperties(){}
	
	protected void initMapControls(){
		getMapWidget().addControl(new SmallMapControl());
		getMapWidget().addControl(new MapTypeControl());
	}
	
	private void buildUi(LatLng clLatLng_) {
		LatLng clLatLng  = clLatLng_;
		if(clLatLng==null){
			ClientLocation cl = AjaxLoader.getClientLocation();
			clLatLng = cl!=null?
					LatLng.newInstance(cl.getLatitude(), cl.getLongitude()):
						LatLng.newInstance(43,1.4)	;
		}
		if(windowWidth<0){
			windowWidth = getWidth();
		}
		map = MapFactory.getInstance().createMapWidgetWithoutCtrl(clLatLng, windowWidth-30, windowHeight-40);
		initMapControls();
		getMapWidget().addMapZoomEndHandler(new MapZoomEndHandler(){

			@Override
			public void onZoomEnd(MapZoomEndEvent event) {
				refreshStoreList();
			}
			
		});
		getMapWidget().addMapDragEndHandler(new MapDragEndHandler(){

			@Override
			public void onDragEnd(MapDragEndEvent event) {
				refreshStoreList();
			}
			
		});
		addItem( getMapWidget() );
		bind();
		
		refreshStoreListDeferred();
	}
	
	private void bind() {
		EventBusManager.getInstance().getEventBus().addHandler(
				StoreResearchByZipCodeChangedEvent.TYPE,
				new StoreResearchByZipCodeChangedEventHandler() {

					@Override
					public void onStoreResearchByZipCode(
							StoreResearchByZipCodeChangedEvent event) {
						onStoreResearchByZipCodeChanged(event.getZipCode());
					}
					
				}
		);
		
		EventBusManager.getInstance().getEventBus().addHandler(
				PlaceDeliverySelectionChangedEvent.TYPE,
				new PlaceDeliverySelectionChangedEventHandler() {
					
					@Override
					public void onStoreSelectionChanged(PlaceDeliverySelectionChangedEvent event) {
						if( event!=null 
								&& event.getPlaceDelivery()!= null
								&& event.getPlaceDelivery().getLatLng() != null){
							StoreMapWindow.this.onStoreSelectionChanged(event.getStore(), event.getPlaceDelivery());
						}
					}
				});
	}
	
	protected void onStoreSelectionChanged(Store st_, PlaceDelivery pd_){
		centerMapOn(LatLngWrapper.converLatLng(pd_.getLatLng()));
		if( pd_ instanceof HomePlaceDelivery ){
			drawHomePlaceDeliveryArea(pd_);
		}
	}
	
	protected void centerMapOn(LatLng center_){
		if(center_!= null){
			getMapWidget().setCenter(center_);
		}
		refreshStoreListDeferred();
	}
	
	private void onStoreResearchByZipCodeChanged(String zipCode_) {
		Geocoder gc = new Geocoder();
		gc.getLatLng(zipCode_ + ", FRANCE", new LatLngCallback() {
			
			@Override
			public void onSuccess(LatLng point) {
				centerMapOn(point);
			}
			
			@Override
			public void onFailure() {
			}
		});
	}
	
	public void refreshStoreListDeferred(){
		//Defer in order to let time to the map to initialized its bound
		// and so to refresh the store list in function of the store bound
		Scheduler.get().scheduleDeferred(new ScheduledCommand() {
		      public void execute() {
		    	  refreshStoreList();
		      }
		    }
		);
	}
	public void refreshStoreList(){
		getMapWidget().clearOverlays();
		legumes.shared.domain.LatLng northEastBound = LatLngWrapper.converLatLng(getMapWidget().getBounds().getNorthEast());
		legumes.shared.domain.LatLng southWestBound = LatLngWrapper.converLatLng(getMapWidget().getBounds().getSouthWest());
		
		final RPCCallSequencer callSequencer = new RPCCallSequencer();
		final GetStoreCallback getStoreCallback = new GetStoreCallback(northEastBound, southWestBound);
		callSequencer.addCall(getStoreCallback);
		callSequencer.addRunnable(new Runnable() {
			public void run() {
				onStoresLoaded(getStoreCallback.getStores());
			}
		});

		callSequencer.callSequence();
	}
	
	private void onStoresLoaded(List<Store> storeList_){
		List<Store> storeList = storeList_;
		if(storeList!=null){
			for(final Store st : storeList){
				List<PlaceDelivery> plList = st.getPlaceDeliveryList();
				if(plList!=null){
					for(final PlaceDelivery pl : plList){
						getMapWidget().addOverlay(createMarker(st, pl));
					}
				}
			}
		}
	}
	
	protected Marker createMarker(final Store st_, final PlaceDelivery pl_){
		MarkerOptions markerOptions = MarkerOptions.newInstance();
		markerOptions.setTitle(pl_.getTitle());
		final boolean isAnHomePlaceDelivery = pl_ instanceof HomePlaceDelivery;
		Icon ic = Icon.DEFAULT_ICON;
		if(isAnHomePlaceDelivery){
			ic.setImageURL( PNResources.INSTANCE.gmapDeliveringAtHomeIcon().getURL() );
		} else {
			ic.setImageURL( PNResources.INSTANCE.gmapDeliveringAtMarketIcon().getURL() );
		}
		markerOptions.setIcon( ic );
			
		final LatLng centLatLng = LatLngWrapper.converLatLng(pl_.getLatLng());
		
		final Marker marker = new Marker(centLatLng);
		marker.setDraggingEnabled(false);
		marker.addMarkerClickHandler(new MarkerClickHandler(){

			@Override
			public void onClick(MarkerClickEvent event) {
				onStoreClicked(st_, pl_, isAnHomePlaceDelivery, centLatLng);
			}

		});
		return marker;
	}
	
	protected void drawHomePlaceDeliveryArea(PlaceDelivery pl_){
		int radius = ((HomePlaceDelivery)pl_).getRadiusKm();
		new MapDrawingCircleUtils(getMapWidget(), LatLngWrapper.converLatLng(pl_.getLatLng()), radius).drawCircle();
	}
	
	protected void showInfoWindow(PlaceDelivery pl_){
		getMapWidget().getInfoWindow().open(map.getCenter(),
				new InfoWindowContent(
						pl_.toString() ));
	}
	
	private void onStoreClicked(Store st_, PlaceDelivery pl_, boolean isAnHomePlaceDelivery_, LatLng centLatLng_){
		if( selectedStore != st_){
			selectedStore = st_;
			onStoreSelected(selectedStore, pl_);
		}
	}
	
	protected void onStoreSelected(Store st_, PlaceDelivery pl_){
		EventBusManager.getInstance().getEventBus().fireEvent(
				new PlaceDeliverySelectionChangedEvent(st_, pl_));
	}
	
	public MapWidget getMapWidget(){
		return map;
	}
}
